﻿using System;
using System.Diagnostics;
using System.Reactive.Linq;
using AlanAamy.Net.TemperatureConverter.Infrastructure.Constants;
using AlanAamy.Net.TemperatureConverter.Models;
using AlanAamy.Net.TemperatureConverter.Services;
using AlanAamy.Net.TemperatureConverter.Views;
using Microsoft.Reactive.Testing;
using Moq;
using NUnit.Framework;

namespace AlanAamy.Net.TemperatureConverter.Tests
{

    [TestFixture]
    public sealed class MainPageViewModelTests
    {
        private TestScheduler _testScheduler;
        private Mock<ILeftSequenceObserverModel> _mockLeftObservableModel;
        private Mock<IRightSequenceObserverModel> _mockRightObservableModel;
        private Mock<ISequenceObserverController> _mockSequenceController;
        private Mock<IMemoryLogAppenderService> _mockMemoryAppenderService;
        private int _onNextRightObservedCount;
        private int _onNextLeftObserverCount;

        [SetUp]
        public void Setup()
        {
            _testScheduler = new TestScheduler();
            _mockRightObservableModel = new Mock<IRightSequenceObserverModel>();
            _mockLeftObservableModel = new Mock<ILeftSequenceObserverModel>();
            _mockMemoryAppenderService = new Mock<IMemoryLogAppenderService>();
            _onNextRightObservedCount = 0;
            _mockLeftObservableModel.Setup(x => x.GetObservable()).Returns(
                () =>
                {
                    return Observable.Create<Decimal>(o =>
                    {
                        Debug.WriteLine("OnNext => RightObserved Count {0}", ++_onNextRightObservedCount);
                        return Observable.Never<Decimal>().StartWith(1M).Subscribe(o);
                    });
                });

            _mockRightObservableModel.Setup(x => x.GetObservable()).Returns(
                () =>
                {
                    return Observable.Create<Decimal>(o =>
                    {
                        Debug.WriteLine("OnNext => LeftObserved {0}", ++_onNextLeftObserverCount);
                        return Observable.Never<Decimal>().StartWith(1M).Subscribe(o);
                    });
                });
            _mockSequenceController = new Mock<ISequenceObserverController>();


        }

        [Test]
        public void CurrentLeftTemperatureUnitChangeWillToggleUnWireSequenceObservers()
        {
              //Arrange
            var viewModel = new MainPageViewModel(_mockMemoryAppenderService.Object, _mockSequenceController.Object,
                _mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 1M);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 0M);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);


            //Act
            viewModel.CurrentLeftTemperatureUnit = TemperatureUnit.Farenheit;

            //Assert
            _mockSequenceController.Verify(_ => _.ToggleUnWireSequenceObservers());
        }

        [Test]
        public void CurrentRightTemperatureUnitChangeWillToggleUnWireSequenceObservers()
        {
            //Arrange
            var viewModel = new MainPageViewModel(_mockMemoryAppenderService.Object, _mockSequenceController.Object,
                _mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 1M);
            _mockLeftObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Celsius);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 0M);
            _mockRightObservableModel.SetupProperty(_ => _.Unit, TemperatureUnit.Farenheit);


            //Act
            viewModel.CurrentRightTemperatureUnit = TemperatureUnit.Celsius;

            //Assert
            _mockSequenceController.Verify(_ => _.ToggleUnWireSequenceObservers());
        }

        [Test]
        public void LeftSequenceChangeSetsLeftModelValue()
        {
            //Arrange
            var viewModel = new MainPageViewModel(_mockMemoryAppenderService.Object, _mockSequenceController.Object,
                _mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockLeftObservableModel.SetupProperty(_ => _.Value, 1M);

            //Act
            viewModel.LeftSequence = 2M;
            
            //Assert
            _mockLeftObservableModel.VerifySet(_ => _.Value = It.IsAny<Decimal>());

        }

        [Test]
        public void RightSequenceChangeSetsRightModelValue()
        {
            //Arrange
            var viewModel = new MainPageViewModel(_mockMemoryAppenderService.Object, _mockSequenceController.Object,
                _mockRightObservableModel.Object,
                _mockLeftObservableModel.Object);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 33.8M);

            bool receivedValueChangeNotification = false;
            _mockLeftObservableModel.SetupSet(_ => _.Value = It.IsAny<Decimal>()).Raises(s => s.ValueChanged += null);
            _mockLeftObservableModel.Object.ValueChanged += (o, e) => receivedValueChangeNotification = !receivedValueChangeNotification;

            //Act
            viewModel.RightSequence = 2M;

            //Assert
            _mockRightObservableModel.VerifySet(_ => _.Value = It.IsAny<Decimal>());
            //Assert.AreEqual(true, receivedValueChangeNotification);

        }

        [Test]
        public void NonNumericRightInputWillBeIgnored()
        {
            //Arrange
            var viewModel = new MainPageViewModel(_mockMemoryAppenderService.Object, _mockSequenceController.Object,
              _mockRightObservableModel.Object,
              _mockLeftObservableModel.Object);
            _mockRightObservableModel.SetupProperty(_ => _.Value, 33.8M);  
        
            //Act
            //Note WPF Converters will convert non numerics to Decimla.MaxValue
            viewModel.RightSequence = Decimal.MaxValue;

            //Assert
            Assert.AreEqual(33.8M, _mockRightObservableModel.Object.Value);
        }
    }
}
